<?php

namespace services\infusion;

use api\modules\v1\forms\infusion\QueueForm;
use common\helpers\CommonHelper;
use common\helpers\DateHelper;
use common\models\clinic\Clinic;
use common\models\patient\DrugRecord;
use common\models\patient\DrugSurplus;
use common\models\patient\InfusionPrintLog;
use common\models\patient\InfusionQueue;
use common\models\patient\InfusionQueueDrug;
use common\models\patient\RevisitingRecord;
use common\models\patient\User;
use services\clinic\TreatmentManageService;
use services\common\ExcelExtendService;
use Yii;
use common\components\Service;
use yii\db\Expression;

/**
 * Class QueueService
 * @package services\infusion
 */
class QueueService extends Service
{
    /**
     * 队列列表
     * @param array $params
     * @param int|null $type
     * @return array
     */
    public function getQueueList(array $params, ?int $type = null): array
    {
        $queueList = $this->getWaitingList($params, $type);
        $passList = $this->getPassList($params, $type);

        $data = array_merge($queueList, $passList);
        $queueIds = array_column($data, 'id');
        // 队列药品
        $queueDrugService = new QueueDrugService();
        $drugList = $queueDrugService->getQueueDrugMapList($queueIds);

        return array_merge($this->getQueueListFormat($data, $drugList), ['now_time' => time(), 'wait_time' => InfusionQueue::WAIT_TIME]);
    }

    /**
     * 队列信息
     * @param int $id
     * @return array
     */
    public function getQueueInfo(int $id): array
    {
        $query = $this->getQueueListQuery();
        $data = $query->where(['iq.id' => $id])
            ->asArray()
            ->one();
        if (empty($data)) {
            return CommonHelper::api(422, '队列不存在');
        }
        $queueIds = $data['id'];
        // 队列药品
        $queueDrugService = new QueueDrugService();
        $drugList = $queueDrugService->getQueueDrugMapList($queueIds);

        $res = $this->getQueueListFormat([$data], $drugList);
        return $data['type'] == InfusionQueue::TYPE_JD ? $res['jd'][0] : $res['jz'][0];
    }

    /**
     * 格式化
     * @param array $data
     * @param array $drugList
     * @return array
     */
    public function getQueueListFormat(array $data, array $drugList): array
    {
        $jdList = $jzList = [];
        $jdWait = $jzWait = [];
        /**
         * @var InfusionQueue[] $lastQueueList
         */
        $lastQueueList = InfusionQueue::find()->where(['id' => array_column($data, 'last_queue_id')])->indexBy('id')->all();
        foreach ($data as $row) {
            // 上一次同配置队列信息
            $lastQueue = $lastQueueList[$row['last_queue_id']] ?? null;
            $printData = self::isPrint($row, $lastQueue);
            $item = [
                'id' => $row['id'],
                'type' => $row['type'],
                'order_num' => $row['order_num'],
                'status' => $row['status'],
                'call_time' => $row['call_time'],
                'is_pass' => $row['status'] == InfusionQueue::STATUS_CALLING && $row['call_time'] < Yii::$app->params['time'] - InfusionQueue::WAIT_TIME ? 1 : 0,// 是否过号，1是、0否
//                'print_py_num' => $row['print_py_num'],
//                'print_xs_num' => $row['print_xs_num'],
                'is_print_py' => $printData['is_print_py'],
                'is_print_xs' => $printData['is_print_xs'],
                'patient' => [
                    'id' => $row['patient_user_id'],
                    'name' => $row['name'],
                    'age' => $row['age'],
                    'gender' => User::$genders[$row['gender']] ?? '',
                    'patient_card_id' => $row['patient_card_id'],
                    'clinic_name' => $row['clinic_name'],
                ],
                'drug_list' => $drugList[$row['id']] ?? [],
            ];

            if ($row['type'] == InfusionQueue::TYPE_JD) {
                $jdList[] = $item;
                if ($item['status'] == InfusionQueue::STATUS_CALLING && $item['is_pass'] == 0) {
                    $jdWait = $item;
                }
            } else {
                $jzList[] = $item;
                if ($item['status'] == InfusionQueue::STATUS_CALLING && $item['is_pass'] == 0) {
                    $jzWait = $item;
                }
            }
        }

        return [
            'jd' => $jdList,
            'jz' => $jzList,
            'jd_wait' => $jdWait,
            'jz_wait' => $jzWait,
        ];
    }

    /**
     * 获取队列是否存在打印信息
     * @param array $data
     * @param InfusionQueue|null $lastQueue
     * @return false[]
     */
    public static function isPrint(array $data, ?InfusionQueue $lastQueue): array
    {
        $res = [
            'is_print_py' => false,
            'is_print_xs' => false,
        ];
        if ($lastQueue && $lastQueue->status != InfusionQueue::STATUS_FINISH) {
            $res['is_print_py'] = $lastQueue->print_py_num > 0;
            $res['is_print_xs'] = $lastQueue->print_xs_num > 0;
        }
        if (empty($res['is_print_py'])) {
            $res['is_print_py'] = $data['print_py_num'] > 0;
        }
        if (empty($res['is_print_xs'])) {
            $res['is_print_xs'] = $data['print_xs_num'] > 0;
        }
        return $res;
    }

    /**
     * 患者等待队列信息
     * @param int $id
     * @return array[]
     */
    public function getPatientWaitingInfo(int $id): array
    {
        // 等待队列
        $data = $this->getWaitingList(['patient_user_id' => $id]);
        $queueIds = array_column($data, 'id');
        // 队列药品
        $queueDrugService = new QueueDrugService();
        $drugList = $queueDrugService->getQueueDrugMapList($queueIds);

        $jdWait = $jzWait = [];
        foreach ($data as $row) {
            $drug = $drugList[$row['id']] ?? [];
            $item = [
                'id' => $row['id'],
                'drug_surplus_id' => array_column($drug, 'drug_surplus_id'),
            ];

            if ($row['type'] == InfusionQueue::TYPE_JD) {
                $jdWait = $item;
            } else {
                $jzWait = $item;
            }
        }

        return [
            'jd_wait' => $jdWait,
            'jz_wait' => $jzWait,
        ];
    }

    /**
     * 队列sql
     * @param array $params
     * @return \yii\db\ActiveQuery
     */
    private function getQueueListQuery(array $params = []): \yii\db\ActiveQuery
    {
        $query = InfusionQueue::find()->alias('iq')
            ->select([
                'iq.id', 'iq.type', 'iq.project_id', 'iq.patient_user_id', 'iq.order_num', 'iq.status', 'iq.call_time', 'iq.print_py_num', 'iq.print_xs_num', 'iq.last_queue_id',
                'u.name', 'u.age', 'u.gender', 'u.patient_card_id', 'u.clinic_id',
                'c.name clinic_name',
            ])
            ->innerJoin(User::tableName().' u', 'u.id = iq.patient_user_id')
            ->leftJoin(Clinic::tableName().' c', 'c.id = u.clinic_id');

        if (isset($params['keyword']) && $params['keyword']) {
            $query->andWhere([
                'or',
                ['u.name' => $params['keyword']],
                ['u.patient_card_id' => $params['keyword']],
            ]);
        }

        return $query;
    }

    /**
     * 等待+进行中队列
     * @param array $params
     * @param int|null $type
     * @return array
     */
    private function getWaitingList(array $params, ?int $type = null): array
    {
        $today = DateHelper::today();

        $query = $this->getQueueListQuery($params);

        $query->andWhere([
                'iq.project_id' => Yii::$app->params['project_id'],
                'iq.status' => [InfusionQueue::STATUS_WAIT, InfusionQueue::STATUS_CALLING],
            ])
            ->andWhere([
                'or',
                ['iq.status' => InfusionQueue::STATUS_WAIT],
                [
                    'and',
                    ['iq.status' => InfusionQueue::STATUS_CALLING],
                    ['>=', 'iq.call_time', Yii::$app->params['time'] - InfusionQueue::WAIT_TIME],
                ],
            ])
            ->andWhere(['>=', 'iq.created_at', $today['start']])
            ->andWhere(['<=', 'iq.created_at', $today['end']]);

        if (is_numeric($type) && in_array($type, [InfusionQueue::TYPE_JD, InfusionQueue::TYPE_JZ])) {
            $query->andWhere(['iq.type' => $type]);
        }
        if (isset($params['patient_user_id']) && $params['patient_user_id']) {
            $query->andWhere(['iq.patient_user_id' => $params['patient_user_id']]);
        }

        $data = $query->orderBy([
                'iq.order_num' => SORT_ASC,
            ])
            ->asArray()
            ->all();

        return $data;
    }

    /**
     * 过号队列
     * @param array $params
     * @param int|null $type
     * @return array
     */
    private function getPassList(array $params, ?int $type = null): array
    {
        $today = DateHelper::today();

        $query = $this->getQueueListQuery($params);

        $query->andWhere([
                'iq.project_id' => Yii::$app->params['project_id'],
                'iq.status' => InfusionQueue::STATUS_CALLING
            ])
            ->andWhere(['>=', 'iq.created_at', $today['start']])
            ->andWhere(['<=', 'iq.created_at', $today['end']])
            ->andWhere(['<', 'iq.call_time', Yii::$app->params['time'] - InfusionQueue::WAIT_TIME]);

        if (is_numeric($type) && in_array($type, [InfusionQueue::TYPE_JD, InfusionQueue::TYPE_JZ])) {
            $query->andWhere(['iq.type' => $type]);
        }

        $data = $query->orderBy([
                'iq.order_num' => SORT_ASC,
            ])
            ->asArray()
            ->all();

        return $data;
    }

    /**
     * 获取患者上次排队队列
     * @param int $projectId
     * @param int $patientId
     * @param array $drugConfigIds
     * @return InfusionQueue|null
     */
    public static function getLastQueue(int $projectId, int $patientId, array $drugConfigIds): ?InfusionQueue
    {
        foreach ($drugConfigIds as $key => $row) {
            $drugConfigIds[$key] = (int)$row;
        }
        sort($drugConfigIds);
        $drugConfigIdsStr = json_encode($drugConfigIds);
        $today = DateHelper::today();
        /**
         * @var InfusionQueue|null $res
         */
        $res = InfusionQueue::find()
            ->where([
                'project_id' => $projectId,
                'patient_user_id' => $patientId,
            ])
            ->andWhere(['drug_config_ids' => new Expression("CAST('{$drugConfigIdsStr}' AS JSON)")])
            ->andWhere(['between', 'created_at', $today['start'], $today['end']])
            ->orderBy(['created_at' => SORT_DESC, 'id' => SORT_DESC])
            ->one();
        return $res;
    }

    /**
     * 新增排队
     * @param array $params
     * @return bool
     * @throws \Exception
     */
    public function create(array $params): array
    {
        $params['project_id'] = Yii::$app->params['project_id'];

        $model = new QueueForm();
        $model->setScenario('create');
        $model->attributes = $params;
        if ($model->validate() === false) {
            throw new \Exception(CommonHelper::analyErr($model->getFirstErrors()));
        }

        // 1.更新过号队列
        $oldQueue = $model->getOldQueueInfo();
        if ($oldQueue) {
            $oldQueue->status = InfusionQueue::STATUS_CANCEL;
            $oldQueue->saveAndCheckResult();
        }

        // 2.创建队列
        $infusionQueue = new InfusionQueue();
        $infusionQueue->project_id = $model->project_id;
        $infusionQueue->patient_user_id = $model->patient_user_id;
        $infusionQueue->type = $model->type;
        $infusionQueue->order_num = $model->getMaxOrderNum() + 1;
        $infusionQueue->status = InfusionQueue::STATUS_WAIT;
        // 上次打印队列ID
        if ($lastQueue = self::getLastQueue($infusionQueue->project_id, $infusionQueue->patient_user_id, $model->getDrugConfigIds())) {
            $infusionQueue->last_queue_id = $lastQueue->id;
            if ($lastQueue->status != InfusionQueue::STATUS_FINISH) {
                $infusionQueue->print_py_num = $lastQueue->print_py_num;
                $infusionQueue->print_xs_num = $lastQueue->print_xs_num;
            }
        }
        $infusionQueue->drug_config_ids = $model->getDrugConfigIds();
        $infusionQueue->saveAndCheckResult();

        // 3.创建队列药品
        $patientDrugSurplusList = $model->getPatientDrugSurplusList();
        $batchQueueData = [];
        foreach ($patientDrugSurplusList as $row) {
            $batchQueueData[] = [
                $infusionQueue->id,
                $infusionQueue->patient_user_id,
                $row['drug_config_id'],
                $row['id'],
                1,
                Yii::$app->params['time'],
                Yii::$app->params['time'],
            ];
        }
        $batchQueueColumn = ['queue_id', 'patient_user_id', 'drug_config_id', 'drug_surplus_id', 'use_num', 'created_at', 'updated_at'];
        Yii::$app->db->createCommand()->batchInsert(InfusionQueueDrug::tableName(), $batchQueueColumn, $batchQueueData)->execute();

        return [
            'id' => $infusionQueue->id,
        ];
    }

    /**
     * 叫号
     * @param array $params
     * @return bool
     * @throws \Exception
     */
    public function call(array $params): array
    {
        $params['project_id'] = Yii::$app->params['project_id'];

        $model = new QueueForm();
        $model->setScenario('call');
        $model->attributes = $params;
        if ($model->validate() === false) {
            return CommonHelper::api('422', CommonHelper::analyErr($model->getFirstErrors()));
        }

        // 1.创建队列
        $infusionQueue = $model->getQueueInfo();
        $infusionQueue->status = InfusionQueue::STATUS_CALLING;
        $infusionQueue->call_time = Yii::$app->params['time'];
        $infusionQueue->saveAndCheckResult();

        return [
            'id' => $infusionQueue->id,
        ];
    }

    /**
     * 输液
     * @param array $params
     * @return bool
     * @throws \Exception
     */
    public function infusion(array $params): array
    {
        $params['project_id'] = Yii::$app->params['project_id'];

        $model = new QueueForm();
        $model->setScenario('infusion');
        $model->attributes = $params;
        if ($model->validate() === false) {
            return CommonHelper::api('422', CommonHelper::analyErr($model->getFirstErrors()));
        }

        // 1.创建队列
        $infusionQueue = $model->getQueueInfo();
        $infusionQueue->status = InfusionQueue::STATUS_FINISH;
        $infusionQueue->finish_time = Yii::$app->params['time'];
        $infusionQueue->saveAndCheckResult();

        // 2.更新患者药品
        // 3.添加药品消耗记录
        $queueDrugSurplusList = $model->getQueueDrugSurplusList();
        $batchRecordData = [];
        foreach ($queueDrugSurplusList as $queueDrugSurplus) {
            DrugSurplus::updateAll([
                'surplus_num' => new Expression("surplus_num - {$queueDrugSurplus->use_num}")
            ], [
                'id' => $queueDrugSurplus->drug_surplus_id
            ]);

            $batchRecordData[] = [
                $infusionQueue->patient_user_id,
                $queueDrugSurplus->drug_config_id,
                DrugRecord::TYPE_USE,
                $queueDrugSurplus->use_num,
                $model->operate_id,
                Yii::$app->params['time'],
                Yii::$app->params['time'],
            ];
        }
        $batchRecordColumn = ['patient_user_id', 'drug_config_id', 'type', 'operate_num', 'operate_id', 'created_at', 'updated_at'];
        Yii::$app->db->createCommand()->batchInsert(DrugRecord::tableName(), $batchRecordColumn, $batchRecordData)->execute();

        // 4.更新患者治疗+输液时间
        User::updateAll([
//            'last_clinic_time' => Yii::$app->params['time'],
            'last_infusion_time' => Yii::$app->params['time'],
        ], [
            'id' => $infusionQueue->patient_user_id
        ]);

        // 5.添加来院记录
        $patient = $model->getPatientInfo();
        ExcelExtendService::toHospitalRecord($patient->project_id, RevisitingRecord::TYPE_INFUSION, Yii::$app->params['time'], $patient->patient_card_id, $patient->clinic_id, $infusionQueue->id);

        // 6.更新患者的上次操作时间
        $treatmentService = new TreatmentService();
        $treatmentService->updatePatientLastOperateTime($infusionQueue->patient_user_id, Yii::$app->params['time']);

        return [
            'id' => $infusionQueue->id,
            'type' => $infusionQueue->type,
        ];
    }

    /**
     * 撤销
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function cancel(array $params): array
    {
        $params['project_id'] = Yii::$app->params['project_id'];

        $model = new QueueForm();
        $model->setScenario('cancel');
        $model->attributes = $params;
        if ($model->validate() === false) {
            return CommonHelper::api('422', CommonHelper::analyErr($model->getFirstErrors()));
        }

        // 1.更新队列
        $infusionQueue = $model->getQueueInfo();
        $infusionQueue->status = InfusionQueue::STATUS_CANCEL;
        $infusionQueue->saveAndCheckResult();

        return [
            'id' => $infusionQueue->id,
        ];
    }
}